package id.co.shuba.spamobile.task;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import id.co.shuba.spamobile.App;
import id.co.shuba.spamobile.Encryptor;
import id.co.shuba.spamobile.Sessions;
import id.co.shuba.spamobile.data.AreaDB;
import id.co.shuba.spamobile.data.BaseDB;
import id.co.shuba.spamobile.data.DSARDB;
import id.co.shuba.spamobile.data.DatabaseHandler;
import id.co.shuba.spamobile.data.FlagFreezeDB;
import id.co.shuba.spamobile.data.HolidayDB;
import id.co.shuba.spamobile.data.MaintenanceVisitDB;
import id.co.shuba.spamobile.data.StandardDB;
import id.co.shuba.spamobile.data.ProspectDB;
import id.co.shuba.spamobile.data.ProspectProgressDB;
import id.co.shuba.spamobile.model.DSARInfo;
import id.co.shuba.spamobile.model.MaintenanceVisitInfo;
import id.co.shuba.spamobile.model.ProspectInfo;
import id.co.shuba.spamobile.model.ProspectProgressInfo;
import id.co.shuba.spamobile.model.UserInfo;
import id.co.shuba.spamobile.services.WebServiceHelper;
import id.co.shuba.spamobile.util.Base64;
import id.co.shuba.spamobile.util.XmlUtil;
import android.app.ProgressDialog;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Path.FillType;
import android.os.AsyncTask;
import android.util.Log;
import android.util.Xml;

public class DownloadMasterOfflineTask extends BaseProgressTask{	
	SQLiteDatabase _db;			
	String ns = null;
	boolean deleteOnComplete = true;	
	
	int progress = 0;
	int i = 0;
	int totalRow = 0;
	
	String aoCode;
	
	OnDownloadCompleteListener _listener;
	
	public void setOnDownloadCompleteListener(OnDownloadCompleteListener listener) {
		_listener = listener;
	}

	public DownloadMasterOfflineTask(SQLiteDatabase db, String aoCode) {	
		_db = db;	
		this.aoCode = aoCode;
	}
	
	private void readData(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "SPA");
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();
            if (name.equals("TBL_MS_STANDARD_COUNT")) {
            	totalRow += XmlUtil.readInt(parser, "TBL_MS_STANDARD_COUNT");
            } else if (name.equals("TBL_MS_PRODUCTS_COUNT")) {
            	totalRow += XmlUtil.readInt(parser, "TBL_MS_PRODUCTS_COUNT");
            } else if (name.equals("TBL_MS_CHANNEL_COUNT")) {
            	totalRow += XmlUtil.readInt(parser, "TBL_MS_CHANNEL_COUNT");
            } else if (name.equals("TBL_MS_HOLIDAY_COUNT")) {
            	totalRow += XmlUtil.readInt(parser, "TBL_MS_HOLIDAY_COUNT");
            } else if (name.equals("TBL_MS_USER_ZIP_COUNT")) {
            	totalRow += XmlUtil.readInt(parser, "TBL_MS_USER_ZIP_COUNT");
            } else if (name.equals("TBL_MS_EXTERNAL_MAPPING_COUNT")) {
            	totalRow += XmlUtil.readInt(parser, "TBL_MS_EXTERNAL_MAPPING_COUNT");
            } else if (name.equals("TBL_MS_FLAG_FREEZE_COUNT")) {
            	totalRow += XmlUtil.readInt(parser, "TBL_MS_FLAG_FREEZE_COUNT");
            } else if (name.equals("TBL_MS_STANDARD")) {
                readTblOption(parser);                
            } else if (name.equals("TBL_MS_PRODUCTS")) {
            	readTblProduct(parser);
            } else if (name.equals("TBL_MS_CHANNEL")) {
            	readTblChannel(parser);
            } else if (name.equals("TBL_MS_HOLIDAY")) {
            	readTblHoliday(parser);
            } else if (name.equals("TBL_MS_USER_ZIP")) {
            	readTblUserZip(parser);
            } else if (name.equals("TBL_MS_EXTERNAL_MAPPING")) {
            	readTblExternalMapping(parser);
            } else if (name.equals("TBL_MS_FLAG_FREEZE")) {
            	readTblFlagFreeze(parser);
            } else {
                skip(parser);
            }
        }                   
    }
	
	private void readTblUserZip(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {			
        parser.require(XmlPullParser.START_TAG, ns, "TBL_MS_USER_ZIP");               
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("MS_USER_ZIP")) {
                readUserZip(parser);
            } else {
                skip(parser);
            }
        }      
    }
	
	private void readTblExternalMapping(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {			
        parser.require(XmlPullParser.START_TAG, ns, "TBL_MS_EXTERNAL_MAPPING");               
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("MS_EXTERNAL_MAPPING")) {
                readExternalMapping(parser);
            } else {
                skip(parser);
            }
        }      
    }
	
	private void readTblFlagFreeze(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {			
        parser.require(XmlPullParser.START_TAG, ns, "TBL_MS_FLAG_FREEZE");               
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("MS_FLAG_FREEZE")) {
                readFlagFreeze(parser);
            } else {
                skip(parser);
            }
        }      
    }
	
	private void readTblOption(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {			
        parser.require(XmlPullParser.START_TAG, ns, "TBL_MS_STANDARD");               
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("MS_STANDARD")) {
                readStandard(parser);
            } else {
                skip(parser);
            }
        }      
    }
	
	private void readTblProduct(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {		
        parser.require(XmlPullParser.START_TAG, ns, "TBL_MS_PRODUCTS");           
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("MS_PRODUCTS")) {
                readProduct(parser);
            } else {
                skip(parser);
            }
        }      
    }
	
	private void readTblChannel(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {		
        parser.require(XmlPullParser.START_TAG, ns, "TBL_MS_CHANNEL");             
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("MS_CHANNEL")) {
                readChannel(parser);
            } else {
                skip(parser);
            }
        }      
    }
	
	private void readTblHoliday(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       						
		parser.require(XmlPullParser.START_TAG, ns, "TBL_MS_HOLIDAY");        	
		
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("MS_HOLIDAY")) {
                readHoliday(parser);
            } else {
                skip(parser);
            }
        }      
    }	
	
	private String readString(XmlPullParser parser, String tag) throws IOException, XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, tag);
        String val = readText(parser);
        parser.require(XmlPullParser.END_TAG, ns, tag);
        return val;
    }
	
	private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
        String result = "";
        if (parser.next() == XmlPullParser.TEXT) {
            result = parser.getText();
            parser.nextTag();
        }
        return result;
    }
	
	private void readStandard(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "MS_STANDARD");        
        
        String group = null;
        String value = null;
        String text = null;
        String type = null;
        String isActive = null;
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("STD_GROUP")) group = readString(parser, "STD_GROUP");     
            else if (name.equals("STD_VALUE")) value = readString(parser, "STD_VALUE");
            else if (name.equals("STD_TEXT")) text = readString(parser, "STD_TEXT");
            else if (name.equals("STD_TYPE")) type = readString(parser, "STD_TYPE");
            else if (name.equals("IS_ACTIVE")) isActive = readString(parser, "IS_ACTIVE");
            else skip(parser);                                   
        }      
        
        StandardDB.addOrUpdateOption(_db, group, value, text, type, BaseDB.getSysDateTime(), isActive);
        
        i++;
        publishProgress("Standard");		
    }	
	
	private void readUserZip(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "MS_USER_ZIP");
        
        String postalCode = null;
        String kodya = null;
        String kecamatan = null;
        String kelurahan = null;        
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("ZIP_CODE")) postalCode = readString(parser, "ZIP_CODE");     
            else if (name.equals("KODYA_KAB")) kodya = readString(parser, "KODYA_KAB");
            else if (name.equals("KECAMATAN")) kecamatan = readString(parser, "KECAMATAN");
            else if (name.equals("KELURAHAN")) kelurahan = readString(parser, "KELURAHAN");
            else if (name.equals("KELURAHAN")) kelurahan = readString(parser, "KELURAHAN");
            else skip(parser);                                   
        }      
        
        AreaDB.addPostalCode(_db, postalCode, kodya, kecamatan, kelurahan, BaseDB.getCurrentDateTime(), aoCode);
        
        i++;
        publishProgress("UserZip");		
    }	
	
	private void publishProgress(String status) {
		progress = (int)(i * 100f /totalRow);		
		publishProgress(progress);
		publishStatus(String.format("Updating %s...%s/%s", status, String.valueOf(i), String.valueOf(totalRow)));
	}
	
	private void readProduct(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "MS_PRODUCTS");
        
        String id = null;
        String productName = null;
        String isActive = null;
        String productType = null;
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();         
            
            if (name.equals("PRODUCT_ID")) id = readString(parser, "PRODUCT_ID");
            else if (name.equals("PRODUCT_NAME")) productName = readString(parser, "PRODUCT_NAME");      
            else if (name.equals("IS_ACTIVE")) isActive = readString(parser, "IS_ACTIVE");
            else if (name.equals("PRODUCT_TYPE")) productType = readString(parser, "PRODUCT_TYPE");
            else skip(parser);                                   
        }      
        
        StandardDB.addOrUpdateOption(_db, StandardDB.GROUP_PRODUCT, id, productName, productType, BaseDB.getCurrentDateTime(), isActive);
        
        i++;
        publishProgress("Product");
    }	
	
	private void readChannel(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "MS_CHANNEL");
        
        String id = null;
        String channelName = null;
        String isActive = null;
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("CHANNEL_ID")) id = readString(parser, "CHANNEL_ID");            
            else if (name.equals("CHANNEL_NAME")) channelName = readString(parser, "CHANNEL_NAME"); 
            else if (name.equals("IS_ACTIVE")) isActive = readString(parser, "IS_ACTIVE");
            else skip(parser);                                   
        }      
        
        StandardDB.addOrUpdateOption(_db, StandardDB.GROUP_CHANNELING2, id, channelName, "", BaseDB.getCurrentDateTime(), isActive);
        
        i++;
        publishProgress("Channel");
    }
	
	private void readHoliday(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "MS_HOLIDAY");
        
        String date = null;
        String holidayName = null;      
        String isActive = null;
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("HOLIDAY_DATE")) date = readString(parser, "HOLIDAY_DATE");            
            else if (name.equals("HOLIDAY_NAME")) holidayName = readString(parser, "HOLIDAY_NAME");
            else if (name.equals("IS_ACTIVE")) isActive = readString(parser, "IS_ACTIVE");
            else skip(parser);                                   
        }               
        
        HolidayDB.addOrUpdateOption(_db, date, holidayName, BaseDB.getCurrentDateTime(), isActive);
        
        i++;
        publishProgress("Holiday");
    }
	
	private void readExternalMapping(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "MS_EXTERNAL_MAPPING");
        
        String externalID = null;
        String channelID = null;      
        String isActive = null;
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("EXTERNAL_ID")) externalID = readString(parser, "EXTERNAL_ID");            
            else if (name.equals("CHANNEL_ID")) channelID = readString(parser, "CHANNEL_ID");
            else if (name.equals("IS_ACTIVE")) isActive = readString(parser, "IS_ACTIVE");
            else skip(parser);                                   
        }               
        
        StandardDB.addOrUpdateExternalMapping(_db, externalID, channelID, BaseDB.getCurrentDateTime(), isActive);        
        
        i++;
        publishProgress("External Mapping");
    }
	
	public int readInt(XmlPullParser parser, String tag) throws IOException, XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, tag);
        String text = readText(parser);
        int val = 0;
        try {
			val = Integer.parseInt(text);
		} catch (Exception e) {		
		}        
        parser.require(XmlPullParser.END_TAG, ns, tag);
        return val;
    }
	
	private void readFlagFreeze(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {       
        parser.require(XmlPullParser.START_TAG, ns, "MS_FLAG_FREEZE");
        
        int flag = 0;
        String field = null;
        String enabled = null;
        String isActive = null;
        
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();            
            if (name.equals("FLAG")) flag = readInt(parser, "FLAG");            
            else if (name.equals("FIELD")) field = readString(parser, "FIELD");
            else if (name.equals("ENABLED")) enabled = readString(parser, "ENABLED");
            else if (name.equals("IS_ACTIVE")) isActive = readString(parser, "IS_ACTIVE");
            else skip(parser);                                   
        }               
                
        FlagFreezeDB.addOrUpdate(_db, flag, field, enabled, BaseDB.getCurrentDateTime(), isActive);            
        
        i++;
        publishProgress("Flag Freeze");
    }
	
	private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
        int depth = 1;
        while (depth != 0) {
            switch (parser.next()) {
            case XmlPullParser.END_TAG:
                    depth--;
                    break;
            case XmlPullParser.START_TAG:
                    depth++;
                    break;
            }
        }
    }
		
	@Override
	protected AsyncTaskResult<Void> doInBackground(Object... params) {		
		boolean success = false;
		File file = (File)params[0];
		FileInputStream fis = null;
		GZIPInputStream gis = null;
		try {					
			String nip = (String)params[1];
			/*
			SecretKeySpec localSecretKeySpec = new SecretKeySpec(
					App.createCompleteKey(nip).getBytes(), Encryptor.ALGO_AES);
			
		    Cipher localCipher = Cipher.getInstance(Encryptor.ENC_AES);
		    IvParameterSpec ivspec = new IvParameterSpec(
					App.IV.getBytes());
		    localCipher.init(Cipher.ENCRYPT_MODE, localSecretKeySpec, ivspec);

		    CipherInputStream in = new CipherInputStream(new FileInputStream((File)params[0]), localCipher);*/							
			
			fis = new FileInputStream(file);
			gis = new GZIPInputStream(fis);
			XmlPullParser parser = Xml.newPullParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
            parser.setInput(gis, null);
            parser.nextTag();            					
			
			readData(parser);			
			
			publishProgress(100);
			publishStatus("Done!");
			
			success = true;
			return new AsyncTaskResult<Void>((Void) null);
		} catch (Exception e) {				
			Log.e("DownloadMasterOffline", e.getMessage());
			publishProgress(0);
			publishStatus(e.getMessage());
			return new AsyncTaskResult<Void>(e);
		} finally {
			try {
				if (gis != null) gis.close();
				if (fis != null) fis.close();
				if (success && deleteOnComplete)
					file.delete();
			} catch (Exception e2) {}
			
		}
	}			
	
	@Override
	protected void onPostExecute(AsyncTaskResult<Void> result) {
		if (_listener != null) {
			_listener.onDownloadComplete(result.getError());
		}
	}
	
	@Override
	public String getTitle() {		
		return "DownloadMasterOfflineTask";
	}
	
	
	@Override
	protected void onCancelled(AsyncTaskResult<Void> result) {		
		super.onCancelled(result);
		
		Log.d("DownloadMasterOfflineTask", "Task cancelled");
	}	
}
